package com.jsftoolkit.gen.info.impl;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;

import javax.faces.component.UIComponent;
import javax.faces.event.FacesListener;

import com.jsftoolkit.base.ResourceInfo;
import com.jsftoolkit.base.TagBase;
import com.jsftoolkit.gen.builder.ComponentBuilder;
import com.jsftoolkit.gen.info.ComponentInfo;
import com.jsftoolkit.gen.info.ComponentInfoProvider;
import com.jsftoolkit.gen.info.ConstantInfo;
import com.jsftoolkit.gen.info.PropertyInfo;
import com.jsftoolkit.utils.Utils;

/**
 * Factory for {@link ComponentInfo} instances derived from a class of
 * constants.
 * 
 * @author noah
 * 
 * @deprecated use {@link ComponentBuilder} and
 *             {@link TemplateComponentInfoProvider} instead.
 */
@Deprecated
public class ConstantsComponentInfoProvider implements ComponentInfoProvider {

	public static final String REGISTER_RENDERER = "REGISTER_RENDERER";

	public static final String PACKAGE = "PACKAGE";

	public static final String CLASS_NAME = "CLASS_NAME";

	public static final String COMPONENT_TYPE = "COMPONENT_TYPE";

	public static final String COMPONENT_FAMILY = "COMPONENT_FAMILY";

	public static final String DEFAULT_RENDERER_TYPE = "DEFAULT_RENDERER_TYPE";

	public static final String SUPER_CLASS = "SUPER_CLASS";

	public static final String ABSTRACT = "ABSTRACT";

	public static final String IMPLEMENTS = "IMPLEMENTS";

	public static final String IMPORTS = "IMPORTS";

	public static final String RENDERER_PACKAGE = "RENDERER_PACKAGE";

	public static final String RENDERER_CLASS_NAME = "RENDERER_CLASS_NAME";

	public static final String RENDERER_ABSTRACT = "RENDERER_ABSTRACT";

	public static final String RENDERER_IMPLEMENTS = "RENDERER_IMPLEMENTS";

	public static final String REGISTER_CLASS = "REGISTER_CLASS";

	/**
	 * String[] of names of attributes that should appear on the tag handler but
	 * do not need a constant or getter/setter pair.
	 */
	public static final String RENDER_ATTRIBS = "RENDER_ATTRIBS";

	public static final String TAG_SUPER = "TAG_SUPER";

	public static final String TAG_PACKAGE = "TAG_PACKAGE";

	public static final String TAG_CLASS_NAME = "TAG_CLASS_NAME";

	public static final String TAG_ABSTRACT = "TAG_ABSTRACT";

	public static final String INCLUDES = "INCLUDES";

	public static final String TEMPLATES = "TEMPLATES";

	public static final String HEAD_TEMPLATE = "HEAD_TEMPLATE";

	public static final String TEMPLATE_ENCODING = "TEMPLATE_ENCODING";

	public static final String FACES_CONFIG = "FACES_CONFIG";

	public static final String TAGLIB_XML = "TAGLIB_XML";

	public static final String TLD = "TLD";

	public static final int PUBLIC_STATIC_FINAL = Modifier.PUBLIC
			| Modifier.STATIC | Modifier.FINAL;

	public static final String NAMESPACE = "NAMESPACE";

	public static final String SHORT_NAME = "SHORT_NAME";

	public static final String TAG_NAME = "TAG_NAME";

	public static final String LISTENERS = "LISTENERS";

	public static final String[] IGNORE_CONSTANTS;

	static {
		Set<String> list = new HashSet<String>();
		for (Field field : ConstantsComponentInfoProvider.class.getFields()) {
			if ((field.getModifiers() & PUBLIC_STATIC_FINAL) == PUBLIC_STATIC_FINAL) {
				list.add(field.getName());
			}
		}
		IGNORE_CONSTANTS = list.toArray(new String[list.size()]);
	}

	private final Class<?> spec;

	public ConstantsComponentInfoProvider(Class<?> spec) {
		super();
		this.spec = spec;
	}

	/**
	 * 
	 * @param spec
	 *            a class containing the above constants
	 * @return a {@link ComponentInfo} instance derived from the constants in
	 *         spec.
	 * @throws Exception
	 */
	@SuppressWarnings("unchecked")
	public void fillIn(ComponentInfo info) {
		String _package = (String) getConstant(PACKAGE, spec);
		if (_package != null) {
			info.setPackage(_package);
		}

		String className = (String) getConstant(CLASS_NAME, spec);
		if (className != null) {
			info.setClassName(className);
		}

		String family = (String) getConstant(COMPONENT_FAMILY, spec);
		if (family != null) {
			info.setFamily(family);
		}
		String type = (String) getConstant(COMPONENT_TYPE, spec);
		if (type != null) {
			info.setType(type);
		}
		String rendererType = (String) getConstant(DEFAULT_RENDERER_TYPE, spec);
		if (rendererType != null) {
			info.setRendererType(rendererType);
		}

		Class<? extends FacesListener>[] listeners = (Class<? extends FacesListener>[]) getConstant(
				LISTENERS, spec);
		info.getListeners().addAll(Utils.asSet(listeners));

		// find the important constants
		for (Field field : spec.getFields()) {
			if ((field.getModifiers() & PUBLIC_STATIC_FINAL) == PUBLIC_STATIC_FINAL) {
				String name = field.getName();
				Object value;
				try {
					value = field.get(null);
				} catch (IllegalAccessException e) {
					// wont happen
					throw new RuntimeException(e);
				}
				if (value instanceof PropertyInfo) {
					PropertyInfo pInfo = (PropertyInfo) value;
					Class<?> c1 = pInfo.getType();
					info.getProperties().put(name, pInfo);
					if (c1 != null && !c1.isPrimitive()) {
						info.addImport(c1);
					}
				} else if (value instanceof String) {
					info.addConstant(new ConstantInfo(name, (String) value));
				}
			}
		}

		// remove the special constants. they are specified elsewhere
		for (String name : IGNORE_CONSTANTS) {
			info.getConstants().remove(new ConstantInfo(name, null));
		}

		info.getImports().addAll(
				Arrays.asList(Utils.getValue((Class<?>[]) getConstant(IMPORTS,
						spec), new Class<?>[0])));

		Object _abstract = getConstant(ABSTRACT, spec);
		if (_abstract != null) {
			info.setAbstract(Boolean.valueOf(Utils.toString(_abstract)));
		}
		Class<? extends UIComponent> superClass = (Class<? extends UIComponent>) getConstant(
				SUPER_CLASS, spec);
		if (superClass != null) {
			info.setSuperClass(superClass);
		}
		info.getInterfaces().addAll(
				Utils.asSet((Class<?>[]) getConstant(IMPLEMENTS, spec)));

		String tagPackage = (String) getConstant(TAG_PACKAGE, spec);
		if (tagPackage != null) {
			info.getTag().setPackage(tagPackage);
		}

		String tagClassName = (String) getConstant(TAG_CLASS_NAME, spec);
		if (tagClassName != null) {
			info.getTag().setClassName(tagClassName);
		}
		info.getRenderer().getAttribs().addAll(
				Utils.asSet((String[]) getConstant(RENDER_ATTRIBS, spec)));

		Object rendererAbstract = getConstant(RENDERER_ABSTRACT, spec);
		if (rendererAbstract != null) {
			info.getRenderer().setAbstract(
					Boolean.valueOf(Utils.toString(rendererAbstract)));
		}
		Class<? extends TagBase> tagSuper = (Class<? extends TagBase>) getConstant(
				TAG_SUPER, spec);
		if (tagSuper != null) {
			info.getTag().setSuperClass(tagSuper);
		}

		String tagAbstract = Utils.toString(getConstant(TAG_ABSTRACT, spec));
		if (tagAbstract != null) {
			info.getTag().setAbstract(Boolean.valueOf(tagAbstract));
		}

		String regRenderer = Utils.toString(
				getConstant(REGISTER_RENDERER, spec), null);
		if (regRenderer != null) {
			info.getConfig().setRegisterRenderer(regRenderer);
		}
		String rendClassName = (String) getConstant(RENDERER_CLASS_NAME, spec);
		if (rendClassName != null) {
			info.getRenderer().setClassName(rendClassName);
		}

		String rendPackage = (String) getConstant(RENDERER_PACKAGE, spec);
		if (rendPackage != null) {
			info.getRenderer().setPackage(rendPackage);
		}

		String[] templates = (String[]) getConstant(TEMPLATES, spec);
		if (templates != null) {
			for (String template : templates) {
				info.getRenderer().addTemplate(template);
			}
		}

		String enc = (String) getConstant(TEMPLATE_ENCODING, spec);
		if (enc != null) {
			info.getRenderer().setTemplateEncoding(enc);
		}

		Object constant = getConstant(INCLUDES, spec);
		if (constant instanceof ResourceInfo[]) {
			for (ResourceInfo resource : (ResourceInfo[]) constant) {
				info.getRenderer().addInclude(resource);
			}
		}

		String facesConfig = Utils.toString(getConstant(FACES_CONFIG, spec),
				null);
		if (facesConfig != null) {
			info.getConfig().setFacesConfig(facesConfig);
		}
		String taglib = Utils.toString(getConstant(TAGLIB_XML, spec), null);
		if (taglib != null) {
			info.getConfig().setTaglibXml(taglib);
		}

		String tld = Utils.toString(getConstant(TLD, spec), null);
		if (tld != null) {
			info.getConfig().setTldFile(tld);
		}
		String namespace = Utils.toString(getConstant(NAMESPACE, spec), null);
		if (namespace != null) {
			info.getConfig().setNamespace(namespace);
		}

		String shortName = Utils.toString(getConstant(SHORT_NAME, spec), null);
		if (shortName != null) {
			info.getConfig().setLibraryShortName(shortName);
		}

		String tagName = Utils.toString(getConstant(TAG_NAME, spec), null);
		if (tagName != null) {
			info.getConfig().setTagName(tagName);
		}

		String registerClass = Utils.toString(
				getConstant(REGISTER_CLASS, spec), null);
		if (registerClass != null) {
			info.getConfig().setRegisterClass(registerClass);
		}

	}

	/**
	 * 
	 * @param name
	 * @param spec
	 * @return the value of the constant with the given name
	 * @throws Exception
	 */
	private Object getConstant(String name, Class<?> spec) {
		try {
			try {
				return spec.getField(name).get(null);
			} catch (NoSuchFieldException e) {
				return null;
			}
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

}